package corejava.corejava1.ch14;

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;

/**
 * This program demonstrates that a thread that runs in parallel with the event dispatch thread can
 * cause errors in Swing components.
 * 
 * @version 1.23 2007-05-17
 * @author Cay Horstmann
 */
public class Ch14_12_SwingThreadTest {
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                SwingThreadFrame frame = new SwingThreadFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setVisible(true);
            }
        });
    }
}

/**
 * This frame has two buttons to fill a combo box from a separate thread. The "Good" button uses the
 * event queue, the "Bad" button modifies the combo box directly.
 */
class SwingThreadFrame extends JFrame {
    public SwingThreadFrame() {
        setTitle("SwingThreadTest");

        final JComboBox combo = new JComboBox();
        combo.insertItemAt(Integer.MAX_VALUE, 0);
        combo.setPrototypeDisplayValue(combo.getItemAt(0));
        combo.setSelectedIndex(0);

        JPanel panel = new JPanel();

        JButton goodButton = new JButton("Good");
        goodButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                new Thread(new GoodWorkerRunnable(combo)).start();
            }
        });
        panel.add(goodButton);
        JButton badButton = new JButton("Bad");
        badButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                new Thread(new BadWorkerRunnable(combo)).start();
            }
        });
        panel.add(badButton);

        panel.add(combo);
        add(panel);
        pack();
    }
}

/**
 * This runnable modifies a combo box by randomly adding and removing numbers. This can result in
 * errors because the combo box methods are not synchronized and both the worker thread and the
 * event dispatch thread access the combo box.
 */
class BadWorkerRunnable implements Runnable {
    public BadWorkerRunnable(JComboBox aCombo) {
        combo = aCombo;
        generator = new Random();
    }

    public void run() {
        try {
            while (true) {
                int i = Math.abs(generator.nextInt());
                if (i % 2 == 0)
                    //不要在事件分配线程以外的线程中直接操作Swing组件
                    combo.insertItemAt(i, 0);
                else if (combo.getItemCount() > 0)
                    combo.removeItemAt(i % combo.getItemCount());
                Thread.sleep(1);    //不必要的，为了效果更明显
            }
        } catch (InterruptedException e) {
        }
    }

    private JComboBox combo;
    private Random    generator;
}

/**
 * This runnable modifies a combo box by randomly adding and removing numbers. In order to ensure
 * that the combo box is not corrupted, the editing operations are forwarded to the event dispatch
 * thread.
 */
class GoodWorkerRunnable implements Runnable {
    public GoodWorkerRunnable(JComboBox aCombo) {
        combo = aCombo;
        generator = new Random();
    }

    public void run() {
        try {
            while (true) {
                // 在自己定义的线程中，正确的改变Swing组件的方法
                EventQueue.invokeLater(new Runnable() {
                    public void run() {
                        int i = Math.abs(generator.nextInt());
                        if (i % 2 == 0)
                            combo.insertItemAt(i, 0);
                        else if (combo.getItemCount() > 0)
                            combo.removeItemAt(i % combo.getItemCount());
                    }
                });
                Thread.sleep(1);    //不必要的，为了效果更明显
            }
        } catch (InterruptedException e) {
        }
    }

    private JComboBox combo;
    private Random    generator;
}
